/**
  * Author: Mei
  * Date: 2020-11-24 10:24
  * Description: 
  *
  */
#include <thread>
#include <condition_variable>
#include <mutex>
#include <vector>
#include <queue>
#include <future>

class ThreadPool {
public:
    explicit ThreadPool(size_t threadNum) : stop_(false) {
        for (size_t i = 0; i < threadNum; ++i) {
            workers_.emplace_back([this]() {
                for (;;) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> ul(mtx_);
                        cv_.wait(ul, [this]() { return stop_ || !tasks_.empty(); });
                        if (stop_ && tasks_.empty()) { return; }
                        task = std::move(tasks_.front());
                        tasks_.pop();
                    }
                    task();
                }
            });
        }
    }

    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> ul(mtx_);
            stop_ = true;
        }
        cv_.notify_all();
        for (auto &worker : workers_) {
            worker.join();
        }
    }
    /**
     * 线程池中最重要的方法是负责将工作添加到队列的一种方法
     *
     * @tparam F
     * @tparam Args
     * @param f
     * @param args
     * @return
     */
    //提交要由池异步执行的函数
    template<typename F, typename... Args>
    auto submit(F &&f, Args &&... args) -> std::future<decltype(f(args...))> {
         //创建一个有界参数的函数准备执行
        //将其封装到共享的ptr中，以便能够复制构造/分配
                                                //将所有参数args绑定到函数f
        auto taskPtr = std::make_shared<std::packaged_task<decltype(f(args...))()>>(
                std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
        {
            std::unique_lock<std::mutex> ul(mtx_);
            if (stop_) { throw std::runtime_error("submit on stopped ThreadPool"); }
            tasks_.emplace([taskPtr]() { (*taskPtr)(); });
        }
        cv_.notify_one();
        return taskPtr->get_future();
    }

private:
    bool stop_;
    std::vector<std::thread> workers_;
    std::queue<std::function<void()>> tasks_;
    std::mutex mtx_;
    std::condition_variable cv_;
};